// Package main is the entry point for the WePayKit application.
// It initializes the core components including logging, health checks,
// and starts the HTTP server.
package main

import (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/getsentry/sentry-go"
	"github.com/we-pay-kit/we-pay-kit/internal/api"
	"github.com/we-pay-kit/we-pay-kit/internal/config"
	"github.com/we-pay-kit/we-pay-kit/internal/health"
	"github.com/we-pay-kit/we-pay-kit/internal/logger"
	"github.com/we-pay-kit/we-pay-kit/internal/metrics"
	"github.com/we-pay-kit/we-pay-kit/internal/middleware"
	"github.com/we-pay-kit/we-pay-kit/internal/migrate"
	"github.com/we-pay-kit/we-pay-kit/internal/wechat"
)

// main is the entry point of the application.
// It initializes the logger, sets up health checks, starts the HTTP server,
// and handles graceful shutdown.
func main() {
	// Initialize the logger
	if err := logger.Init(); err != nil {
		fmt.Printf("Failed to initialize logger: %v\n", err)
		os.Exit(1)
	}
	defer logger.Sync()

	// CLI flags
	migrateDir := flag.String("migrate", "", "run SQL migrations from the given directory and exit (e.g., ./migrations)")
	modeTest := flag.Bool("test", false, "run in test mode with mocked WeChat API")
	modeProd := flag.Bool("prod", false, "run in prod mode with real WeChat API")
	flag.Parse()

	cfg := config.Get()
	// Initialize Sentry if DSN provided
	if cfg.Sentry.Dsn != "" {
		_ = sentry.Init(sentry.ClientOptions{Dsn: cfg.Sentry.Dsn, Environment: cfg.Sentry.Environment, SampleRate: cfg.Sentry.SampleRate})
		defer sentry.Flush(2 * time.Second)
	}

	// Run migrations if requested
	if *migrateDir != "" {
		logger.Info("Running database migrations...")
		if err := migrate.ApplyMigrations(*migrateDir); err != nil {
			logger.Error(fmt.Sprintf("Migration failed: %v", err))
			os.Exit(1)
		}
		logger.Info("Migrations completed successfully")
		return
	}

	// Print console output prompt
	fmt.Printf("[%s] > WePayKit Running ...\n", time.Now().Format("2006-01-02 15:04:05"))

	logger.Info("Starting WePayKit application")

	// Decide WeChat API implementation based on mode
	var wapi wechat.API
	if *modeProd {
		wconf := cfg.WechatPay
		wapi = wechat.NewClient(&wechat.Config{AppID: wconf.AppID, MchID: wconf.MchID, APIv3Key: wconf.APIv3Key, SerialNo: wconf.SerialNo, NotifyURL: wconf.NotifyURL, PrivateKeyPEM: wconf.PrivateKeyPEM})
		// Start certificate manager for production mode
		if client, ok := wapi.(*wechat.Client); ok {
			client.StartCertificateManager(context.Background())
		}
		logger.Info("Running in PROD mode: using Real WeChat API client with certificate management")
	} else if *modeTest {
		wapi = wechat.NewMockClient()
		logger.Info("Running in TEST mode: using Mock WeChat API")
	} else {
		// default to PROD-like (real client)
		wconf := cfg.WechatPay
		wapi = wechat.NewClient(&wechat.Config{AppID: wconf.AppID, MchID: wconf.MchID, APIv3Key: wconf.APIv3Key, SerialNo: wconf.SerialNo, NotifyURL: wconf.NotifyURL, PrivateKeyPEM: wconf.PrivateKeyPEM})
		// Start certificate manager for default mode
		if client, ok := wapi.(*wechat.Client); ok {
			client.StartCertificateManager(context.Background())
		}
		logger.Info("No mode specified, defaulting to PROD mode with certificate management")
	}

	// Build handler chain
	mux := setupRoutes(wapi)
	root := middleware.Recovery(middleware.RequestLogger(metrics.Instrument(middleware.CORS(mux))))

	// Create HTTP server
	server := &http.Server{
		Addr:    ":8080",
		Handler: root,
	}

	// Start server in a goroutine
	go func() {
		logger.Info("Starting HTTP server on :8080")
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			logger.Error(fmt.Sprintf("HTTP server error: %v", err))
		}
	}()

	// Wait for interrupt signal to gracefully shutdown the server
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit

	logger.Info("Shutting down server...")

	// Create a context with timeout for graceful shutdown
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Attempt graceful shutdown
	if err := server.Shutdown(ctx); err != nil {
		logger.Error(fmt.Sprintf("Server forced to shutdown: %v", err))
	}

	logger.Info("Server exiting")
}

// setupRoutes configures the HTTP routes for the application.
// It registers the health check endpoint and any other API endpoints.
func setupRoutes(wapi wechat.API) *http.ServeMux {
	mux := http.NewServeMux()

	// Create health check service and register the health check endpoint
	healthService := health.NewService()
	// Register default health checkers for database, cache, and message queue
	healthService.RegisterDefaults()

	mux.HandleFunc("/health", healthService.Handler())
	mux.Handle("/metrics", metrics.Handler())

	cfg := config.Get()
	// API v1 with auth + rate limit + idempotency
	auth := middleware.APIKeyAuth(middleware.AuthConfig{Keys: cfg.Auth.APIKeys})
	rl := middleware.NewRateLimiter(10, 20)
	idem := middleware.NewIdempotency(middleware.IdemConfig{TTL: 10 * time.Minute})

	activity := api.NewActivityHandler()
	mux.Handle("/api/v1/activities", auth(rl.Middleware(idem.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodPost:
			activity.CreateActivity(w, r)
		case http.MethodGet:
			activity.ListActivities(w, r)
		case http.MethodPatch:
			activity.UpdateStatus(w, r)
		case http.MethodDelete:
			activity.DeleteActivity(w, r)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})))))

	np := api.NewNumberPackageHandler()
	mux.Handle("/api/v1/number-packages", auth(rl.Middleware(idem.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodPost:
			np.CreatePackage(w, r)
		case http.MethodGet:
			np.ListPackages(w, r)
		case http.MethodDelete:
			np.DeletePackage(w, r)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})))))
	mux.Handle("/api/v1/number-packages/upload", auth(rl.Middleware(idem.Middleware(http.HandlerFunc(np.UploadDetailsCSV)))))
	mux.Handle("/api/v1/number-packages/details", auth(rl.Middleware(http.HandlerFunc(np.ListDetails))))

	trx := api.NewTransactionHandler()
	mux.Handle("/api/v1/transactions", auth(rl.Middleware(idem.Middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.Method {
		case http.MethodPost:
			trx.CreateTransaction(w, r)
		case http.MethodGet:
			trx.ListTransactions(w, r)
		case http.MethodPatch:
			trx.UpdateStatus(w, r)
		default:
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})))))
	mux.Handle("/api/v1/transactions/stats", auth(rl.Middleware(http.HandlerFunc(trx.Stats))))

	// Marketing handlers
	mkt := api.NewMarketingHandler(wapi)
	mux.Handle("/api/v1/marketing/cash-coupons/send", auth(rl.Middleware(http.HandlerFunc(mkt.SendCashCoupon))))
	mux.Handle("/api/v1/marketing/cash-coupons/list", auth(rl.Middleware(http.HandlerFunc(mkt.ListCashCouponIssues))))

	rec := api.NewReconSettleHandler()
	mux.Handle("/api/v1/reconciliation/run", auth(rl.Middleware(http.HandlerFunc(rec.RunReconciliation))))
	mux.Handle("/api/v1/reconciliation/data", auth(rl.Middleware(http.HandlerFunc(rec.GetReconciliation))))
	mux.Handle("/api/v1/settlements/create", auth(rl.Middleware(http.HandlerFunc(rec.CreateSettlement))))
	mux.Handle("/api/v1/settlements/add-detail", auth(rl.Middleware(http.HandlerFunc(rec.AddSettlementDetail))))
	mux.Handle("/api/v1/settlements/status", auth(rl.Middleware(http.HandlerFunc(rec.UpdateSettlementStatus))))
	mux.Handle("/api/v1/settlements/get", auth(rl.Middleware(http.HandlerFunc(rec.GetSettlement))))
	mux.Handle("/api/v1/settlements/list", auth(rl.Middleware(http.HandlerFunc(rec.ListSettlements))))
	mux.Handle("/api/v1/settlements/details", auth(rl.Middleware(http.HandlerFunc(rec.ListSettlementDetails))))

	return mux
}
